﻿#include "OA_ArgParser.h"
#include "OA_ErrorNumber.h"
#include <cctype>
#include <algorithm>
#include <sstream>

namespace OpenAPIxx {
    struct ArgInfo {
        ArgInfo() : omitable(true), type(ARGTYPE_FLAG) {}
        std::string genUsage() {
            std::string ret, name = argNames[0];
            switch (type) {
            case ARGTYPE_FLAG:          ret = name; break;
            case ARGTYPE_STRING:         ret = name + " " + meta; break;
            case ARGTYPE_STRINGS:   ret = name + " " + meta + " ..."; break;
            default:                    break;
            }
            if (omitable == true) {
                ret = "[" + ret + "]";
            }
            return ret;
        }

        bool isSet;                         // 要解析的参数中是否设置过此参数
        bool omitable;                      // 是否可省略
        ArgType type;                       // 参数类型
        std::vector<std::string> argNames;  // 参数的所有名称（长、短参数）
        std::string discription;            // 描述
        std::string meta;                   // META值
        ArgValue defaultValue;              // 默认值
    };
}

OpenAPIxx::ArgValue::ArgValue() : boolVal(false), type(ARGTYPE_UNKNOWN) {}

OpenAPIxx::ArgValue::ArgValue(bool b) : boolVal(b), type(ARGTYPE_FLAG) {}

OpenAPIxx::ArgValue::ArgValue(const std::string& str) : boolVal(false), strVal(str), type(ARGTYPE_STRING) {}

OpenAPIxx::ArgValue::ArgValue(const char* str) : boolVal(false), strVal(str), type(ARGTYPE_STRING) {}

OpenAPIxx::ArgValue::ArgValue(const std::vector<std::string>& strs) : boolVal(false), strsVal(strs), type(ARGTYPE_STRINGS) {}

OpenAPIxx::ArgValue::ArgValue(const OpenAPIxx::ArgValue& v) : boolVal(v.boolVal), strVal(v.strVal), strsVal(v.strsVal), type(v.type) {}

OpenAPIxx::ArgValue& OpenAPIxx::ArgValue::operator=(bool value) {
    boolVal = value;
    type = ARGTYPE_FLAG;
    return *this;
}

OpenAPIxx::ArgValue& OpenAPIxx::ArgValue::operator=(const char* value) {
    return this->operator=(std::string(value));
}

OpenAPIxx::ArgValue& OpenAPIxx::ArgValue::operator=(const std::string& value) {
    strVal = value;
    type = ARGTYPE_STRING;
    return *this;
}

OpenAPIxx::ArgValue& OpenAPIxx::ArgValue::operator=(const std::vector<std::string>& value) {
    strsVal = value;
    type = ARGTYPE_STRINGS;
    return *this;
}

OpenAPIxx::ArgValue& OpenAPIxx::ArgValue::operator=(const ArgValue& value) {
    strVal = value.strVal;
    strsVal = value.strsVal;
    type = value.type;
    return *this;
}

void OpenAPIxx::ArgValue::reset() { boolVal = false; strVal = ""; type = ARGTYPE_UNKNOWN; }

OpenAPIxx::Args::~Args() {
    clear();
}

OpenAPIxx::ArgValue& OpenAPIxx::Args::operator[](const std::string& name) {
    auto it = m_argsMap.find(name);
    if (it == m_argsMap.end()) {
        ArgValue* value = new ArgValue();
        m_argsMap[name] = value;
        m_args.push_back(value);
        return *(value);
    }
    return *(it->second);
}

void OpenAPIxx::Args::clear() {
    for (size_t i = 0; i < m_args.size(); i++) {
        delete m_args[i];
    }
    m_args.clear();
    m_argsMap.clear();
}

bool OpenAPIxx::Args::hasArg(const std::string& name) {
    return m_argsMap.find(name) != m_argsMap.end();
}

int OpenAPIxx::Args::combineArgs(const std::vector<std::string>& argNames) {
    if (argNames.size() < 2) {
        return OA_ERR_SUCCESS;
    }
    if (m_argsMap.find(argNames[0]) == m_argsMap.end()) {
        return OA_ERR_OPERATION_FAILED;
    }

    ArgValue* value = m_argsMap[argNames[0]];
    for (size_t i = 1; i < argNames.size(); i++) {
        m_argsMap[argNames[i]] = value;
    }

    return OA_ERR_SUCCESS;
}

bool OpenAPIxx::ArgValue::getBoolValue() const {
    if(type != ARGTYPE_FLAG) {
        throw "type not match!";
    }
    return boolVal;
}

std::string OpenAPIxx::ArgValue::getStringValue() const {
    if(type != ARGTYPE_STRING) {
        throw "type not match!";
    }
    return strVal;
}

std::vector<std::string> OpenAPIxx::ArgValue::getStringsValue() const {
    if(type != ARGTYPE_STRINGS) {
        throw "type not match!";
    }
    return strsVal;
}

OpenAPIxx::ArgType OpenAPIxx::ArgValue::getType() const { return type; }

OpenAPIxx::ArgParser::ArgParser() : m_programName("ProgramName") {}

OpenAPIxx::ArgParser::ArgParser(const std::string& programName) : m_programName(programName) {}

OpenAPIxx::ArgParser::~ArgParser() {
    clear();
}

int OpenAPIxx::ArgParser::setArg(const std::string& name, ArgType type, bool omitable,
    const ArgValue& defaultValue, const std::string& discription, const std::string& meta) {
    return setMultiArg({ name }, type, omitable, defaultValue, discription, meta);
}

int OpenAPIxx::ArgParser::setMultiArg(const std::vector<std::string>& names, ArgType type, bool omitable,
    const ArgValue& defaultValue, const std::string& discription, const std::string& meta) {
    ArgInfo* info = new ArgInfo();
    for (auto it = names.begin(); it != names.end(); it++) {
        if (this->_isValidArgFormat(*it) == false) {
            return OA_ERR_ILLEGAL_ARG;
        }
        m_argNameInfoMap[*it] = info;
    }
    m_argsInfo.push_back(info);

    // 保存參數信息
    info->isSet = false;
    info->argNames = names;
    info->type = type;
    info->defaultValue = defaultValue;
    info->omitable = omitable;
    info->discription = discription;
    info->meta = meta;
    if(type != defaultValue.getType()) {
        return OA_ERR_ILLEGAL_ARG;
    }

    return OA_ERR_SUCCESS;
}

#define OAARGPARSE_STATUS_ARG 0
#define OAARGPARSE_STATUS_VAL -1
int OpenAPIxx::ArgParser::parseArg(int argc, const char** argv, Args& args, std::string& errorMessage) {
    args.clear();
    errorMessage = "";
    for (ArgInfo* info : m_argsInfo) {
        info->isSet = false;
    }

    // 遍歷傳入的參數
    // 使用狀態機控制當前解析參數還是參數的值
    int status = OAARGPARSE_STATUS_ARG;
    std::string curArg;
    bool isShortArgs;
    std::map<std::string, ArgInfo*>::iterator infoIt;
    for (int i = 1; i < argc; i++) {
        if(status == OAARGPARSE_STATUS_ARG) {
            if(_isValidArgFormat(argv[i]) == false) {
                errorMessage = std::string("invalid arg format: ") + argv[i];
                return OA_ERR_OPERATION_FAILED;
            }
            // 獲取參數名
            isShortArgs = false;
            auto splitedArg = _splitArg(argv[i]);
            if (splitedArg.size() > 1) {
                isShortArgs = true;
            }
            for(std::string& oneArg : splitedArg) {
                // 參數是否存在
                infoIt = m_argNameInfoMap.find(oneArg);
                if(infoIt == m_argNameInfoMap.end()) {
                    errorMessage = std::string("unknow arg: ") + (oneArg);
                    return OA_ERR_OPERATION_FAILED;
                }
                // 为了兼容有多个参数名的参数，所有参数名默认都使用第0个名字
                // 在解析过程的最后把其余参数名补全
                oneArg = infoIt->second->argNames[0];
                curArg = oneArg;
                // 檢查簡寫短參數的類型
                switch (infoIt->second->type) {
                case ARGTYPE_FLAG:
                    args[oneArg] = true;
                    m_argNameInfoMap[oneArg]->isSet = true;
                    break;
                case ARGTYPE_STRING:
                case ARGTYPE_STRINGS:
                    if (isShortArgs == true) {
                        errorMessage = std::string("format error: ") + argv[i];
                        return OA_ERR_OPERATION_FAILED;
                    }
                    status = OAARGPARSE_STATUS_VAL;
                    break;
                default:
                    errorMessage = std::string("internel error, unknown type:") + std::to_string(infoIt->second->type);
                    return OA_ERR_OPERATION_FAILED;
                }
            } // for splitedArg
        }
        else { // status == OAARGPARSE_STATUS_VAL
            // 保存value
            if (infoIt->second->type == ARGTYPE_STRINGS) {
                if(args.hasArg(curArg) == false) {
                    args[curArg] = std::vector<std::string>({ std::string(argv[i]) });
                }
                else {
                    args[curArg].strsVal.push_back(argv[i]);
                }
            } else { // type == ARGTYPE_STRING
                args[curArg] = argv[i];
            }
            m_argNameInfoMap[curArg]->isSet = true;
            status = OAARGPARSE_STATUS_ARG;
        }
    } // foreach argv
    // 缺少value的情況
    if (status == OAARGPARSE_STATUS_VAL) {
        errorMessage = std::string("value is missing! arg:") + curArg;
        return OA_ERR_OPERATION_FAILED;
    }
    // 給未出現的參數設置默認值
    for(ArgInfo* info : m_argsInfo) {
        if (info->isSet == false) {
            if (info->omitable == false) {
                errorMessage = "missing arg: " + info->argNames[0];
                return OA_ERR_OPERATION_FAILED;
            }
            args[info->argNames[0]] = info->defaultValue;
        }
    }
    // 对有多个参数名的参数补全未出现的参数名
    for (ArgInfo* info : m_argsInfo) {
        if (info->argNames.size() > 1) {
            args.combineArgs(info->argNames);
        }
    }

    return OA_ERR_SUCCESS;
}

std::string OpenAPIxx::ArgParser::generateHelpMessage() {
    // 必選參數
    // 可選FLAG類型短參數
    // 其他參數

    std::stringstream ss;
    std::vector<std::string> nonOmitableArgs, omitableShortArgs, otherArgs;
    std::string name;
    ArgInfo* info;
    for (size_t i = 0; i < m_argsInfo.size(); i++) {
        info = m_argsInfo[i];
        name = info->argNames[0];
        if (info->omitable == false) {
            nonOmitableArgs.push_back(name);
        }
        else if (name.length() == 2 && info->type == ARGTYPE_FLAG) {
            omitableShortArgs.push_back(name);
        }
        else {
            otherArgs.push_back(name);
        }
    }
    std::sort(nonOmitableArgs.begin(), nonOmitableArgs.end());
    std::sort(omitableShortArgs.begin(), omitableShortArgs.end());
    std::sort(otherArgs.begin(), otherArgs.end());

    ss << "Usage: "<< std::endl <<"    " << m_programName << ' ';
    for (size_t i = 0; i < nonOmitableArgs.size(); i++) {
        ss << m_argNameInfoMap[nonOmitableArgs[i]]->genUsage() << ' ';
    }
    std::string allShortOmitableFlagArg;
    for (size_t i = 0; i < omitableShortArgs.size(); i++) {
        allShortOmitableFlagArg.push_back(omitableShortArgs[i][1]);
    }
    if (allShortOmitableFlagArg.length() > 0) {
        ss << "[-" << allShortOmitableFlagArg << "] ";
    }
    for (size_t i = 0; i < otherArgs.size(); i++) {
        ss << m_argNameInfoMap[otherArgs[i]]->genUsage() << ' ';
    }
    
    ss << std::endl << "Options:" << std::endl;
    std::string args;
    for (size_t i = 0; i < m_argsInfo.size(); i++) {
        args.clear();
        for (size_t j = 0; j < m_argsInfo[i]->argNames.size(); j++) {
            args += m_argsInfo[i]->argNames[j] + ", ";
        }
        args.pop_back();
        args.pop_back();
        ss << args << std::endl << "    " << m_argsInfo[i]->discription << std::endl;
    }
    return ss.str();
}

void OpenAPIxx::ArgParser::clear() {
    m_argNameInfoMap.clear();
    for (size_t i = 0; i < m_argsInfo.size(); i++) {
        delete m_argsInfo[i];
    }
    m_programName.clear();
}

bool OpenAPIxx::ArgParser::_isValidArgFormat(const std::string& arg) {
    if (arg.length() < 2
    || arg == "--"
    || arg[0] != '-') {
        return false;
    }

    // 檢查“-”後面的內容的合法性，取值範圍a-z,A-Z,0-9,_
    size_t i = 1;
    if (arg[1] == '-') {
        i = 2;
    }
    for (; i < arg.length(); i++) {
        if (std::isalnum(arg[i]) == false
        && arg[i] != '_') {
            return false;
        }
    }
    return true;
}

// 用於獲取多個連續的flag類型短參數
std::vector<std::string> OpenAPIxx::ArgParser::_splitArg(const char* arg) {
    std::vector<std::string> ret;
    std::string arg_str(arg);

    // 多個flag短參數
    if (arg_str.length() > 2 && arg_str[1] != '-') {
        for (size_t i = 1; i < arg_str.length(); i++) {
            ret.push_back(std::string("-") + arg_str[i]);
        }
    }
    // 一個參數
    else {
        ret.push_back(arg_str);
    }

    return ret;
}

